install.packages('ggplot2')
Installing package into <U+393C><U+3E31>C:/Users/joebstl.b/Documents/R/win-library/3.3<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.3/ggplot2_2.2.1.zip'
Content type 'application/zip' length 2761887 bytes (2.6 MB)
downloaded 2.6 MB
package ‘ggplot2’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\joebstl.b\AppData\Local\Temp\RtmpmKOH82\downloaded_packages

Loan data

This data set contains 113,937 loans with 81 variables on each loan, including loan amount, borrower rate (or interest rate), current loan status, borrower income, borrower employment status, borrower credit history, and the latest payment information. The explanation of the variables can be found there: https://www.prosper.com/Downloads/Services/Documentation/ProsperDataExport_Details.html

library(ggplot2)
Paket <U+393C><U+3E31>ggplot2<U+393C><U+3E32> wurde unter R Version 3.3.3 erstellt
loans <- read.csv('prosperloanData.csv')
head(loans)

Loan original amount

ggplot(aes(x = LoanOriginalAmount), data = loans) + 
  geom_histogram(color = ('#24323e'), fill = ('#02ccba')) +
  ggtitle('Histogram of original loan amount')

summary(loans$LoanOriginalAmount)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1000    4000    6500    8337   12000   35000 

The median of the loans is 6500. I suggest the money is needed for extra expenses due to unexpected problems, like repairs at home or taking a small loan for a holiday.

Loans by year

# Convert date to year ---------------------------
loans$LoanOriginationYear <- format(as.Date(loans$LoanOriginationDate, format="%Y-%m-%d"),"%Y")
ggplot(aes(x = LoanOriginationYear, y = LoanOriginalAmount, fill = Term,
           group = LoanOriginationYear), data = loans) +
    geom_bar(aes(group = LoanOriginationYear), position = 'dodge', stat = 'identity')+
    ggtitle("Loans by Year") +
    labs(x = "Loan origination year", y = "Loan original amount")

In the plot above we can see that from 2006 to 2010 the loans where about 25000 and short term. Then in 2011 people took the same amount of loan, but with a longer term. In 2012 even more people neede long term loans. In 2013 and 2014 people needed higher long term loans.

Home owner

No we want to take a look at the home owners In the next step we want to proof that people who aren t home owners need more often small loans for vacation, home improvement or household expenses than home owners.

summary(loans$IsBorrowerHomeowner)
False  True 
56459 57478 

Our suggestion that house owners need less loans, seem to be wrong. Almost half the amount of borrowers are house owners. Now we want to limit the loan amount to a smaller range, because we want to know if house owners also need smaller loans, for home improvments or others. ###Boxplot homeowner - limited loan original amount

  qplot(x = IsBorrowerHomeowner, y = LoanOriginalAmount,
        data = loans, geom = 'boxplot') +
    scale_y_continuous(limits = c(1000, 20000))

In the plot above we can see that house owners need bigger amounts of money than non house owners.

Next we want to see, if there is a relation between the prosper rate and the fact that the borrower is a house owner. Normally a house owner has a better rating, due to more financial security. ###Boxplot homeowner - Prosper score

qplot(x=IsBorrowerHomeowner, y=ProsperScore,
        data=loans, geom = 'boxplot') +
        scale_y_continuous()

Another surprise here. The Prosper score is almost the same for both kinds of borrowers.

Current Delinquencies by home owner

summary(loans$MonthlyLoanPayment)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0   131.6   217.7   272.5   371.6  2252.0 
ggplot(aes(x=CurrentDelinquencies, y=..count../sum(..count..)), data = subset(loans, !is.na(IsBorrowerHomeowner))) +
  geom_freqpoly(aes(color = IsBorrowerHomeowner)) + 
  xlab('CurrentDelinquencies') + 
  ylab('Percentage of Borrowers with current delinquencies')

In the plot above we can not notice any difference between home owners and non home owners in current delinquencies.

Category of Loan

To get a better readability, we are going to map the numeric values to better readable strings according to this site: https://www.prosper.com/Downloads/Services/Documentation/ProsperDataExport_Details.html

#transform the numeric value of categories to readable names--------------
loans$Category[loans$ListingCategory..numeric. == '0'] <- 'Not Available'
loans$Category[loans$ListingCategory..numeric. == '1'] <- 'Debt Consolidation'
loans$Category[loans$ListingCategory..numeric. == '2'] <- 'Home Improvement'
loans$Category[loans$ListingCategory..numeric. == '3'] <- 'Business'
loans$Category[loans$ListingCategory..numeric. == '4'] <- 'Personal Loan'
loans$Category[loans$ListingCategory..numeric. == '5'] <- 'Student Use'
loans$Category[loans$ListingCategory..numeric. == '6'] <- 'Auto'
loans$Category[loans$ListingCategory..numeric. == '7'] <- 'Other'
loans$Category[loans$ListingCategory..numeric. == '8'] <- 'Baby & Adoption'
loans$Category[loans$ListingCategory..numeric. == '9'] <- 'Boat'
loans$Category[loans$ListingCategory..numeric. == '10'] <- 'Cosmetic Procedure'
loans$Category[loans$ListingCategory..numeric. == '11'] <- 'Engagement Ring'
loans$Category[loans$ListingCategory..numeric. == '12'] <- 'Green Loans'
loans$Category[loans$ListingCategory..numeric. == '13'] <- 'Household Expenses'
loans$Category[loans$ListingCategory..numeric. == '14'] <- 'Large Purchases'
loans$Category[loans$ListingCategory..numeric. == '15'] <- 'Medical/Dental'
loans$Category[loans$ListingCategory..numeric. == '16'] <- 'Motorcycle'
loans$Category[loans$ListingCategory..numeric. == '17'] <- 'RV'
loans$Category[loans$ListingCategory..numeric. == '18'] <- 'Taxes'
loans$Category[loans$ListingCategory..numeric. == '19'] <- 'Vacation'
loans$Category[loans$ListingCategory..numeric. == '20'] <- 'Wedding Loans'
reorder_size <- function(x) {
  factor(x, levels = names(sort(table(x))))
}
ggplot(data = subset(loans,
                    LoanOriginalAmount > 2500
                    & LoanOriginalAmount <= 9000
                    & Category != 'Debt Consolidation'
                    & Category != 'Not Available'
                    & Category != 'Other'),
       aes(reorder_size(Category))) +
      geom_bar(colour='#24323e', fill='#02ccba')+
      ggtitle("Amount of loans by category") +
      theme(axis.text.x=element_text(angle=45,hjust=1,vjust=0.5))+
      labs(x="", y = "Number of loans") +
      coord_flip()

As the most categories are ‘debt consolidation’, ‘not availabe’ and ‘other’, we cant really tell if it is like suggested. To get a better picture, we excludet those categories in the plot above. The data is not complete. But mostly money is needed for household expenses or home improvement.

Occupations of Borrowers

head(loans$Occupation)
[1] Other         Professional  Other         Skilled Labor Executive    
[6] Professional 
68 Levels:  Accountant/CPA Administrative Assistant Analyst ... Waiter/Waitress

Because there are 68 different types of occupation we are going to combine groups into a new data frame into bigger occupation groups.

#summarize different occupations into grouped occupations----------------
loans$GroupedOccupation <- factor(loans$Occupation)
levels(loans$GroupedOccupation) <- list(
  Student=c("Student - College Graduate Student",
"Student - College Senior", 
"Student - Community College",
"Student - College Freshman",
"Student - College Junior",
"Student - College Sophomore",
"Student - Technical School"), 
Medical_Health=c("Doctor", "Nurse's Aide",
                 "Nurse (RN)",
                 "Nurse (LPN)",
                 "Dentist",
                 "Pharmacist",
                 "Medical Technician",
                 "Psychologist"),
Sales=c("Sales - Commission",
        "Sales - Retail",
        "Car Dealer",
        "Realtor"),
Service=c("Food Service Management",
          "Food Service",
          "Postal Service",
          "Social Worker",
          "Truck Driver",
          "Bus Driver",
          "Retail Management",
          "Waiter/Waitress",
          "Flight Attendant",
          "Clerical",
          "Religious",
          "Clergy"),
Laborer=c("Construction",
          "Laborer",
          "Skilled Labor",
          "Landscaping",
          "Homemaker",
          "Fireman",
          "Executive",
          "Teacher's Aide",
          "Computer Programmer",
          "Administrative Assistant",
          "Professional",
          "Accountant/CPA",
          "Tradesman - Carpenter",
            "Tradesman - Mechanic",
            "Tradesman - Electrician",
            "Tradesman - Plumber",
          "Pilot - Private/Commercial"),
HigherEdJobs=c("Architect",
               "Biologist",
               "Engineer - Electrical",
               "Engineer - Mechanical",
               "Engineer - Chemical",
               "Judge", "Teacher",
               "Scientist",
               "Professor",
               "Attorney", "Analyst", "Accountant/CPA"
               ),
CivilService=c("Civil Service",
               "Military Officer",
               "Police Officer/Correction Officer",
               "Military Enlisted"),
Other=c("Other", "")
)
ggplot(data=subset(loans, GroupedOccupation != 'Other' & !is.na(GroupedOccupation)), x=GroupedOccupation, aes(reorder_size(GroupedOccupation))) +
    geom_bar(colour='#24323e', fill='#02ccba')+
    ggtitle("Borrowers by Occupation")+
    labs(x="", y = "Number of loans")+
    theme(axis.text.x=element_text(angle=45,hjust=1,vjust=0.5))

Let us take a closer look, how much money is needed, depending on the fact that a borrower is /is not a home owner and his occupational group.

Loan amount by grouped occupation and homeowner status

ggplot(aes(x = IsBorrowerHomeowner, y = LoanOriginalAmount),
      data = loans) +
      stat_summary(fun.y = mean, geom = 'point', shape = 4)

ggplot(aes(x = GroupedOccupation, y = LoanOriginalAmount),
      data = loans) +
      theme(axis.text.x = element_text(angle=45,hjust=0.5,vjust=0.5))+
      geom_point(aes(color = IsBorrowerHomeowner))

In the plot above, we can see that stundents need the smaller amounts of money. The higher the loan gets, the more homeowners are the borrowers.

To get better information, we want to limit the loan to a maximum of 5000. ###Limited loan amount by grouped occupation and homeowner status

ggplot(aes(x = GroupedOccupation, y = LoanOriginalAmount),
      data = loans) +
      theme(axis.text.x = element_text(angle = 45,hjust = 0.5,vjust = 0.5))+
      geom_point(aes(color = IsBorrowerHomeowner))+
      scale_y_continuous(limits = c(1000, 5000))

For money less than 5000 the majority of the borrowers are not home owners. Although in some occupational groups there are a lot of home owners. This may be caused by our occupational grouping, which contains professions with a wide income range. For example in the group ‘Medical_Health’, there are doctors and nurses etc.

Prosper rating

#-----------------create a new DF with info about the prosper rating and the amount
library(dplyr)

Attache Paket: <U+393C><U+3E31>dplyr<U+393C><U+3E32>

The following objects are masked from <U+393C><U+3E31>package:stats<U+393C><U+3E32>:

    filter, lag

The following objects are masked from <U+393C><U+3E31>package:base<U+393C><U+3E32>:

    intersect, setdiff, setequal, union
creditsByGrade <- group_by(loans, ProsperRating..numeric.)
creditsByGrade <- summarise(creditsByGrade,
    mean_amount = mean(LoanOriginalAmount), 
    median_amount = median(LoanOriginalAmount),
    min_amount = min(LoanOriginalAmount),
    max_amount = max(LoanOriginalAmount),
    n = n()) 
creditsByGrade

Borrower Rate - Prosper Score

ggplot(aes(x=BorrowerRate, y=ProsperScore), data = loans)+
  geom_line()+
  geom_smooth()+
  ggtitle("Line Plot of borrower rate and prosper score")

No surprises here, the better the Prosper Score, the better the borrower rate.

Prosper rating if the income is verifiable

We suggest that the prosper rating is better if the income is verifiable.

ggplot(aes(x = ProsperRating..numeric., y = ..count../sum(..count..)), data = subset(loans, !is.na(IncomeVerifiable))) +
  geom_freqpoly(aes(color=IncomeVerifiable)) + 
  xlab('Prosper Rating') + 
  ylab('Percentage of Borrowers with that Prosper Rating')+
  ggtitle("Prosper rating in percent by verifiable income")

Yes, our suggestion is right.

Employment status duration

Next we want to take a look at the credit grade and the employment status duration. We suggest the longer the employment status, the better is the credit grade.

summary(loans$EmploymentStatusDuration)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   0.00   26.00   67.00   96.07  137.00  755.00    7625 
ggplot(aes(x = CreditGrade, y = EmploymentStatusDuration),
       data = loans) +
  geom_point(aes(color =  
                  EmploymentStatus))+
  ggtitle("Credit grade by employment status duration")

As we can see in the plot above, most of the loan takers are full-time employees. But also there is a lot of Data missing. To get a clearer picture, we want do save the employment status duration into buckets.

# save employment status duration into buckets-----------------------------
loans$bucket_EmploymentStatusDuration <- cut(loans$EmploymentStatusDuration,
                               c(0, 12, 24, 36, 48, 50, 62, 74, 86, 98, 110, 122, 134, 146, 755))
ggplot(aes(x = CreditGrade, y = bucket_EmploymentStatusDuration), data = loans) + 
  geom_point(aes(color = EmploymentStatus))+
  ggtitle("Credit grade by employment status duration in buckets")

This plot is not much better, altough it is easier to read. We can see that there is data missing and that most of the loan takers are full time employees.

ggplot(aes(x = LoanOriginalAmount, fill = LoanStatus), data = loans) +
    facet_wrap(~Term) +
    geom_histogram(aes(color = LoanStatus)) +
    scale_fill_brewer(type = 'qual') +
    ggtitle("Histogram of loan Amounts by status and terms")

In the plot above we can see that most of the loans are mid-term.

Debt to income ratio

ggplot(data = loans, aes(x = DebtToIncomeRatio)) +                
        geom_histogram(colour = '#24323e', fill = '#02ccba', binwidth = 0.005) +
        xlim(0, quantile(loans$DebtToIncomeRatio, prob = 0.5, na.rm = TRUE)) +
        ggtitle("Debt To Income Ratio") +
        xlab("Debt to Income Ratio") +
        ylab("Count")

        summary(loans$loan_income_ratio)
Length  Class   Mode 
     0   NULL   NULL 

Lender yield

We suggest that there is a higher lender yield, if the borrower rate is higher.

library(dplyr)
ggplot(loans, aes(x = LenderYield, y = BorrowerRate)) +
  geom_point(alpha = 1/20, colour = '#02ccba') +
  scale_x_continuous(limits = c(0, quantile(loans$LenderYield, 0.75))) +
  scale_y_continuous( 
                     limits = c(0 , quantile(loans$BorrowerRate, 0.75))) +
  ggtitle('Lender yield and borrower rate')

Yes, we can see clearly the higher the borrower rate, the higher the lender yield.

Lender yield by number of Investors

ggplot(aes(x = BorrowerRate, y = Investors), data=loans) + 
  geom_point(aes(color=LenderYield))+
  ylim(0, 600)+
  ggtitle('Lender yield by number of investors')

In the plot above we can see quite well, that the investors yield gets higher the higher the borrower rate gets.

ggplot(aes(x = loans$Investors, y = ..count..), data = loans) +
  geom_freqpoly(aes(color = Investors), binwidth=0.1) + 
  scale_x_continuous(limits = c(0, 250), breaks = seq(0, 50, 250)) +
  scale_y_continuous(breaks = seq(0, 750, 50))+
  ylim(0, 750)+
  xlim(1, 250)+
  xlab('number of Investors') + 
  ylab('count') +
  ggtitle('Number of Investors per Loan')
Scale for 'y' is already present. Adding another scale for 'y', which will
replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will
replace the existing scale.

The majority of the Investors is just one person. In the plot above, we limit the number of loans given to 1500 in order to get a better picture of loans given by more then one investor.

Current delinquencies by credit grade

    qplot(x=CreditGrade, y=DelinquenciesLast7Years,
        data=loans, geom='boxplot')+
    ylim(0, 25)

As we can see in the plot above, the better the credit grade, less delinquenices.

Loan amount by term, grouped by grouped occupation and category

ggplot(aes(x = Term, y = LoanOriginalAmount), data = loans) +
    facet_wrap(~GroupedOccupation) +
    geom_point(aes(color = Category)) +
    scale_y_continuous(limits = c(1000, 5000))

The plot above gives us a nice overview. As we can see most of the loans are mid term loans with a duration of 36 month. The usages of the loans are well mixed.

ggplot(aes(EstimatedReturn, EstimatedLoss), 
       data=subset(loans, GroupedOccupation != 'Other' 
                   & !is.na(GroupedOccupation)
                   & !is.na(IncomeRange)
                   & IncomeRange != 'Not displayed'
                  & IncomeRange != 'Not employed'))+
  geom_point(aes(size=IncomeRange, colour=GroupedOccupation))+
  ggtitle('Estimated loss and estimated return by income range of borrower')

The plot above gives an overview of the estimated return and estimated loss by grouped occupation and income range. We can see that there is a small group where the estimated loss is high and there is not an estimated return. But we can’t see that this is happening only to a special occuption group or income range in there. Most of the estimated returns and estimated losses are between 0 and 0.1.

Because the plot is hard to read due to the density of information, we will take a closer look at on occupation group - the students.

ggplot(aes(EstimatedReturn, EstimatedLoss), 
       data=subset(loans, GroupedOccupation == 'Student' 
                   & IncomeRange != 'Not displayed'
                  & IncomeRange != 'Not employed'))+
  geom_point(aes(size=IncomeRange, colour=IncomeRange))+
  ggtitle('Estimated loss and estimated return by income range for students')

This plot shows that there are some students that seem to earn already a lot of money, altought the majority earns between $1 - 24999.

ggplot(loans, aes(LP_CustomerPayments, LP_InterestandFees)) +
  geom_point(aes(colour =LP_ServiceFees), size = 1) +
  coord_equal()

This plot shows that the lower the customer payments are the lower the service fees and interest fees are.

Heatmap

ggplot(data = loans, aes(x = Category, y = GroupedOccupation)) +
  geom_tile(aes(fill = LoanOriginalAmount)) +
        theme(axis.text.x=element_text(angle=60,hjust=1,vjust=0.9))

In the plot above we get a nice overview of the category of loans and the grouped occupations.

Summary

Because of the big amount of variables it took some time, to read through the explanations of the prosper loan data. To get started we explored some different variables. In order to get nice plots, we had to convert some values. For example the origin date to year, the numeric categories into readable categories and the job duration months where summarized in buckets It was interresting to see that from 2011 on borrowers needed higher loans with longer terms. It was quite a surprise that there is no big difference between home owners and non home owners, because we suggested that home owners are financially more strong and don’t need small loans. For the other variables I could not find a lot of surprising facts. For example the worse the credit grade is, the higher the delinquencies in the last 7 years are or that the lender yield gets lower the higher the numbe of investors get. It would be nice if there were not big groups like ‘na’ or ‘other’ in the occupation and category group. Maybe there could be also data about the age and gender of the borrower provided, which may lead to interesting findings.

LS0tDQp0aXRsZTogIkRBNCBFREEgTE9BTiBEQVRBIiANCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoJ2dncGxvdDInKQ0KYGBgDQojI0xvYW4gZGF0YQ0KVGhpcyBkYXRhIHNldCBjb250YWlucyAxMTMsOTM3IGxvYW5zIHdpdGggODEgdmFyaWFibGVzIG9uIGVhY2ggbG9hbiwgaW5jbHVkaW5nIGxvYW4gYW1vdW50LCBib3Jyb3dlciByYXRlIChvciBpbnRlcmVzdCByYXRlKSwgY3VycmVudCBsb2FuIHN0YXR1cywgYm9ycm93ZXIgaW5jb21lLCBib3Jyb3dlciBlbXBsb3ltZW50IHN0YXR1cywgYm9ycm93ZXIgY3JlZGl0IGhpc3RvcnksIGFuZCB0aGUgbGF0ZXN0IHBheW1lbnQgaW5mb3JtYXRpb24uDQpUaGUgZXhwbGFuYXRpb24gb2YgdGhlIHZhcmlhYmxlcyBjYW4gYmUgZm91bmQgdGhlcmU6DQpodHRwczovL3d3dy5wcm9zcGVyLmNvbS9Eb3dubG9hZHMvU2VydmljZXMvRG9jdW1lbnRhdGlvbi9Qcm9zcGVyRGF0YUV4cG9ydF9EZXRhaWxzLmh0bWwNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbG9hbnMgPC0gcmVhZC5jc3YoJ3Byb3NwZXJsb2FuRGF0YS5jc3YnKQ0KaGVhZChsb2FucykNCmBgYA0KDQojIyNMb2FuIG9yaWdpbmFsIGFtb3VudA0KYGBge3IgMSAtIEhvdyBtdWNoIG1vbmV5IGlzIG5lZWRlZH0NCmdncGxvdChhZXMoeCA9IExvYW5PcmlnaW5hbEFtb3VudCksIGRhdGEgPSBsb2FucykgKyANCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAoJyMyNDMyM2UnKSwgZmlsbCA9ICgnIzAyY2NiYScpKSArDQogIGdndGl0bGUoJ0hpc3RvZ3JhbSBvZiBvcmlnaW5hbCBsb2FuIGFtb3VudCcpDQoNCnN1bW1hcnkobG9hbnMkTG9hbk9yaWdpbmFsQW1vdW50KQ0KYGBgDQpUaGUgbWVkaWFuIG9mIHRoZSBsb2FucyBpcyA2NTAwLiBJIHN1Z2dlc3QgdGhlIG1vbmV5IGlzIG5lZWRlZCBmb3IgZXh0cmEgZXhwZW5zZXMgZHVlIHRvIHVuZXhwZWN0ZWQgcHJvYmxlbXMsIGxpa2UgcmVwYWlycyBhdCBob21lIG9yIHRha2luZyBhIHNtYWxsIGxvYW4gZm9yIGEgaG9saWRheS4NCg0KIyMjTG9hbnMgYnkgeWVhcg0KYGBge3IgMiAtIExvYW5zIG92ZXIgdGltZSBieSB0ZXJtfQ0KIyBDb252ZXJ0IGRhdGUgdG8geWVhciAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmxvYW5zJExvYW5PcmlnaW5hdGlvblllYXIgPC0gZm9ybWF0KGFzLkRhdGUobG9hbnMkTG9hbk9yaWdpbmF0aW9uRGF0ZSwgZm9ybWF0PSIlWS0lbS0lZCIpLCIlWSIpDQoNCmdncGxvdChhZXMoeCA9IExvYW5PcmlnaW5hdGlvblllYXIsIHkgPSBMb2FuT3JpZ2luYWxBbW91bnQsIGZpbGwgPSBUZXJtLA0KICAgICAgICAgICBncm91cCA9IExvYW5PcmlnaW5hdGlvblllYXIpLCBkYXRhID0gbG9hbnMpICsNCiAgICBnZW9tX2JhcihhZXMoZ3JvdXAgPSBMb2FuT3JpZ2luYXRpb25ZZWFyKSwgcG9zaXRpb24gPSAnZG9kZ2UnLCBzdGF0ID0gJ2lkZW50aXR5JykrDQogICAgZ2d0aXRsZSgiTG9hbnMgYnkgWWVhciIpICsNCiAgICBsYWJzKHggPSAiTG9hbiBvcmlnaW5hdGlvbiB5ZWFyIiwgeSA9ICJMb2FuIG9yaWdpbmFsIGFtb3VudCIpDQpgYGANCkluIHRoZSBwbG90IGFib3ZlIHdlIGNhbiBzZWUgdGhhdCBmcm9tIDIwMDYgdG8gMjAxMCB0aGUgbG9hbnMgd2hlcmUgYWJvdXQgMjUwMDAgYW5kIHNob3J0IHRlcm0uIFRoZW4gaW4gMjAxMSBwZW9wbGUgdG9vayB0aGUgc2FtZSBhbW91bnQgb2YgbG9hbiwgYnV0IHdpdGggYSBsb25nZXIgdGVybS4gSW4gMjAxMiBldmVuIG1vcmUgcGVvcGxlIG5lZWRlIGxvbmcgdGVybSBsb2Fucy4gSW4gMjAxMyBhbmQgMjAxNCBwZW9wbGUgbmVlZGVkIGhpZ2hlciBsb25nIHRlcm0gbG9hbnMuIA0KDQojIyNIb21lIG93bmVyDQpObyB3ZSB3YW50IHRvIHRha2UgYSBsb29rIGF0IHRoZSBob21lIG93bmVycw0KSW4gdGhlIG5leHQgc3RlcCB3ZSB3YW50IHRvIHByb29mIHRoYXQgcGVvcGxlIHdobyBhcmVuIHQgaG9tZSBvd25lcnMgbmVlZCBtb3JlIG9mdGVuIHNtYWxsIGxvYW5zIGZvciB2YWNhdGlvbiwgaG9tZSBpbXByb3ZlbWVudCBvciBob3VzZWhvbGQgZXhwZW5zZXMgdGhhbiBob21lIG93bmVycy4NCmBgYHtyfQ0Kc3VtbWFyeShsb2FucyRJc0JvcnJvd2VySG9tZW93bmVyKQ0KYGBgDQoNCk91ciBzdWdnZXN0aW9uIHRoYXQgaG91c2Ugb3duZXJzIG5lZWQgbGVzcyBsb2Fucywgc2VlbSB0byBiZSB3cm9uZy4gQWxtb3N0IGhhbGYgdGhlIGFtb3VudCBvZiBib3Jyb3dlcnMgYXJlIGhvdXNlIG93bmVycy4NCk5vdyB3ZSB3YW50IHRvIGxpbWl0IHRoZSBsb2FuIGFtb3VudCB0byBhIHNtYWxsZXIgcmFuZ2UsIGJlY2F1c2Ugd2Ugd2FudCB0byBrbm93IGlmIGhvdXNlIG93bmVycyBhbHNvIG5lZWQgc21hbGxlciBsb2FucywgZm9yIGhvbWUgaW1wcm92bWVudHMgb3Igb3RoZXJzLg0KIyMjQm94cGxvdCBob21lb3duZXIgLSAgbGltaXRlZCBsb2FuIG9yaWdpbmFsIGFtb3VudA0KYGBge3IgMyAtIFJlbGF0aW9uIGJldHdlZW4gcHJvc3BlciBzY29yZSBhbmQgaG9tZW93bmVyfQ0KICBxcGxvdCh4ID0gSXNCb3Jyb3dlckhvbWVvd25lciwgeSA9IExvYW5PcmlnaW5hbEFtb3VudCwNCiAgICAgICAgZGF0YSA9IGxvYW5zLCBnZW9tID0gJ2JveHBsb3QnKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMTAwMCwgMjAwMDApKQ0KYGBgDQpJbiB0aGUgcGxvdCBhYm92ZSB3ZSBjYW4gc2VlIHRoYXQgaG91c2Ugb3duZXJzIG5lZWQgYmlnZ2VyIGFtb3VudHMgb2YgbW9uZXkgdGhhbiBub24gaG91c2Ugb3duZXJzLg0KDQpOZXh0IHdlIHdhbnQgdG8gc2VlLCBpZiB0aGVyZSBpcyBhIHJlbGF0aW9uIGJldHdlZW4gdGhlIHByb3NwZXIgcmF0ZSBhbmQgdGhlIGZhY3QgdGhhdCB0aGUgYm9ycm93ZXIgaXMgYSBob3VzZSBvd25lci4gTm9ybWFsbHkgYSBob3VzZSBvd25lciBoYXMgYSBiZXR0ZXIgcmF0aW5nLCBkdWUgdG8gbW9yZSBmaW5hbmNpYWwgc2VjdXJpdHkuDQojIyNCb3hwbG90IGhvbWVvd25lciAtIFByb3NwZXIgc2NvcmUNCmBgYHtyIDQgLSByZWxhdGlvbiBiZXR3ZWVuIHByb3NwZXIgc2NvcmUgYW5kIGhvbWV3bmVyfQ0KcXBsb3QoeD1Jc0JvcnJvd2VySG9tZW93bmVyLCB5PVByb3NwZXJTY29yZSwNCiAgICAgICAgZGF0YT1sb2FucywgZ2VvbSA9ICdib3hwbG90JykgKw0KICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoKQ0KYGBgDQpBbm90aGVyIHN1cnByaXNlIGhlcmUuIFRoZSBQcm9zcGVyIHNjb3JlIGlzIGFsbW9zdCB0aGUgc2FtZSBmb3IgYm90aCBraW5kcyBvZiBib3Jyb3dlcnMuDQoNCiMjI0N1cnJlbnQgRGVsaW5xdWVuY2llcyBieSBob21lIG93bmVyDQpgYGB7ciA1IC0gY3VycmVudCBkZWxpbnF1ZW5jaWVzIGlmIGhvbWVvd25lciB9DQpzdW1tYXJ5KGxvYW5zJE1vbnRobHlMb2FuUGF5bWVudCkNCmdncGxvdChhZXMoeD1DdXJyZW50RGVsaW5xdWVuY2llcywgeT0uLmNvdW50Li4vc3VtKC4uY291bnQuLikpLCBkYXRhID0gc3Vic2V0KGxvYW5zLCAhaXMubmEoSXNCb3Jyb3dlckhvbWVvd25lcikpKSArDQogIGdlb21fZnJlcXBvbHkoYWVzKGNvbG9yID0gSXNCb3Jyb3dlckhvbWVvd25lcikpICsgDQogIHhsYWIoJ0N1cnJlbnREZWxpbnF1ZW5jaWVzJykgKyANCiAgeWxhYignUGVyY2VudGFnZSBvZiBCb3Jyb3dlcnMgd2l0aCBjdXJyZW50IGRlbGlucXVlbmNpZXMnKQ0KYGBgDQpJbiB0aGUgcGxvdCBhYm92ZSB3ZSBjYW4gbm90IG5vdGljZSBhbnkgZGlmZmVyZW5jZSBiZXR3ZWVuIGhvbWUgb3duZXJzIGFuZCBub24gaG9tZSBvd25lcnMgaW4gY3VycmVudCBkZWxpbnF1ZW5jaWVzLg0KDQojIyNDYXRlZ29yeSBvZiBMb2FuDQpUbyBnZXQgYSBiZXR0ZXIgcmVhZGFiaWxpdHksIHdlIGFyZSBnb2luZyB0byBtYXAgdGhlIG51bWVyaWMgdmFsdWVzIHRvIGJldHRlciByZWFkYWJsZSBzdHJpbmdzIGFjY29yZGluZyB0byB0aGlzIHNpdGU6IGh0dHBzOi8vd3d3LnByb3NwZXIuY29tL0Rvd25sb2Fkcy9TZXJ2aWNlcy9Eb2N1bWVudGF0aW9uL1Byb3NwZXJEYXRhRXhwb3J0X0RldGFpbHMuaHRtbA0KDQpgYGB7ciBsb2FucyBieSBjYXRlZ29yeSB9DQojdHJhbnNmb3JtIHRoZSBudW1lcmljIHZhbHVlIG9mIGNhdGVnb3JpZXMgdG8gcmVhZGFibGUgbmFtZXMtLS0tLS0tLS0tLS0tLQ0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMCddIDwtICdOb3QgQXZhaWxhYmxlJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMSddIDwtICdEZWJ0IENvbnNvbGlkYXRpb24nDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcyJ10gPC0gJ0hvbWUgSW1wcm92ZW1lbnQnDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICczJ10gPC0gJ0J1c2luZXNzJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnNCddIDwtICdQZXJzb25hbCBMb2FuJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnNSddIDwtICdTdHVkZW50IFVzZScNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzYnXSA8LSAnQXV0bycNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzcnXSA8LSAnT3RoZXInDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICc4J10gPC0gJ0JhYnkgJiBBZG9wdGlvbicNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzknXSA8LSAnQm9hdCcNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzEwJ10gPC0gJ0Nvc21ldGljIFByb2NlZHVyZScNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzExJ10gPC0gJ0VuZ2FnZW1lbnQgUmluZycNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzEyJ10gPC0gJ0dyZWVuIExvYW5zJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTMnXSA8LSAnSG91c2Vob2xkIEV4cGVuc2VzJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTQnXSA8LSAnTGFyZ2UgUHVyY2hhc2VzJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTUnXSA8LSAnTWVkaWNhbC9EZW50YWwnDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxNiddIDwtICdNb3RvcmN5Y2xlJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTcnXSA8LSAnUlYnDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxOCddIDwtICdUYXhlcycNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzE5J10gPC0gJ1ZhY2F0aW9uJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMjAnXSA8LSAnV2VkZGluZyBMb2FucycNCmBgYA0KDQoNCmBgYHtyIDYgLSBXaHkgaXMgdGhlIG1vbmV5IG5lZWRlZH0NCnJlb3JkZXJfc2l6ZSA8LSBmdW5jdGlvbih4KSB7DQogIGZhY3Rvcih4LCBsZXZlbHMgPSBuYW1lcyhzb3J0KHRhYmxlKHgpKSkpDQp9DQoNCmdncGxvdChkYXRhID0gc3Vic2V0KGxvYW5zLA0KICAgICAgICAgICAgICAgICAgICBMb2FuT3JpZ2luYWxBbW91bnQgPiAyNTAwDQogICAgICAgICAgICAgICAgICAgICYgTG9hbk9yaWdpbmFsQW1vdW50IDw9IDkwMDANCiAgICAgICAgICAgICAgICAgICAgJiBDYXRlZ29yeSAhPSAnRGVidCBDb25zb2xpZGF0aW9uJw0KICAgICAgICAgICAgICAgICAgICAmIENhdGVnb3J5ICE9ICdOb3QgQXZhaWxhYmxlJw0KICAgICAgICAgICAgICAgICAgICAmIENhdGVnb3J5ICE9ICdPdGhlcicpLA0KICAgICAgIGFlcyhyZW9yZGVyX3NpemUoQ2F0ZWdvcnkpKSkgKw0KICAgICAgZ2VvbV9iYXIoY29sb3VyPScjMjQzMjNlJywgZmlsbD0nIzAyY2NiYScpKw0KICAgICAgZ2d0aXRsZSgiQW1vdW50IG9mIGxvYW5zIGJ5IGNhdGVnb3J5IikgKw0KICAgICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEsdmp1c3Q9MC41KSkrDQogICAgICBsYWJzKHg9IiIsIHkgPSAiTnVtYmVyIG9mIGxvYW5zIikgKw0KICAgICAgY29vcmRfZmxpcCgpDQpgYGANCkFzIHRoZSBtb3N0IGNhdGVnb3JpZXMgYXJlICdkZWJ0IGNvbnNvbGlkYXRpb24nLCAnbm90IGF2YWlsYWJlJyBhbmQgJ290aGVyJywgd2UgY2FudCByZWFsbHkgdGVsbCBpZiBpdCBpcyBsaWtlIHN1Z2dlc3RlZC4gVG8gZ2V0IGEgYmV0dGVyIHBpY3R1cmUsIHdlIGV4Y2x1ZGV0IHRob3NlIGNhdGVnb3JpZXMgaW4gdGhlIHBsb3QgYWJvdmUuIFRoZSBkYXRhIGlzIG5vdCBjb21wbGV0ZS4gQnV0IG1vc3RseSBtb25leSBpcyBuZWVkZWQgZm9yIGhvdXNlaG9sZCBleHBlbnNlcyBvciBob21lIGltcHJvdmVtZW50LiANCg0KIyMjT2NjdXBhdGlvbnMgb2YgQm9ycm93ZXJzDQpgYGB7cn0NCmhlYWQobG9hbnMkT2NjdXBhdGlvbikNCmBgYA0KQmVjYXVzZSB0aGVyZSBhcmUgNjggZGlmZmVyZW50IHR5cGVzIG9mIG9jY3VwYXRpb24gd2UgYXJlIGdvaW5nIHRvIGNvbWJpbmUgZ3JvdXBzIGludG8gYSBuZXcgZGF0YSBmcmFtZSBpbnRvIGJpZ2dlciBvY2N1cGF0aW9uIGdyb3Vwcy4NCmBgYHtyIDcgLSBXaG8gbmVlZGVzIHRoZSBtb25leX0NCiNzdW1tYXJpemUgZGlmZmVyZW50IG9jY3VwYXRpb25zIGludG8gZ3JvdXBlZCBvY2N1cGF0aW9ucy0tLS0tLS0tLS0tLS0tLS0NCmxvYW5zJEdyb3VwZWRPY2N1cGF0aW9uIDwtIGZhY3Rvcihsb2FucyRPY2N1cGF0aW9uKQ0KbGV2ZWxzKGxvYW5zJEdyb3VwZWRPY2N1cGF0aW9uKSA8LSBsaXN0KA0KICBTdHVkZW50PWMoIlN0dWRlbnQgLSBDb2xsZWdlIEdyYWR1YXRlIFN0dWRlbnQiLA0KIlN0dWRlbnQgLSBDb2xsZWdlIFNlbmlvciIsIA0KIlN0dWRlbnQgLSBDb21tdW5pdHkgQ29sbGVnZSIsDQoiU3R1ZGVudCAtIENvbGxlZ2UgRnJlc2htYW4iLA0KIlN0dWRlbnQgLSBDb2xsZWdlIEp1bmlvciIsDQoiU3R1ZGVudCAtIENvbGxlZ2UgU29waG9tb3JlIiwNCiJTdHVkZW50IC0gVGVjaG5pY2FsIFNjaG9vbCIpLCANCk1lZGljYWxfSGVhbHRoPWMoIkRvY3RvciIsICJOdXJzZSdzIEFpZGUiLA0KICAgICAgICAgICAgICAgICAiTnVyc2UgKFJOKSIsDQogICAgICAgICAgICAgICAgICJOdXJzZSAoTFBOKSIsDQogICAgICAgICAgICAgICAgICJEZW50aXN0IiwNCiAgICAgICAgICAgICAgICAgIlBoYXJtYWNpc3QiLA0KICAgICAgICAgICAgICAgICAiTWVkaWNhbCBUZWNobmljaWFuIiwNCiAgICAgICAgICAgICAgICAgIlBzeWNob2xvZ2lzdCIpLA0KU2FsZXM9YygiU2FsZXMgLSBDb21taXNzaW9uIiwNCiAgICAgICAgIlNhbGVzIC0gUmV0YWlsIiwNCiAgICAgICAgIkNhciBEZWFsZXIiLA0KICAgICAgICAiUmVhbHRvciIpLA0KU2VydmljZT1jKCJGb29kIFNlcnZpY2UgTWFuYWdlbWVudCIsDQogICAgICAgICAgIkZvb2QgU2VydmljZSIsDQogICAgICAgICAgIlBvc3RhbCBTZXJ2aWNlIiwNCiAgICAgICAgICAiU29jaWFsIFdvcmtlciIsDQogICAgICAgICAgIlRydWNrIERyaXZlciIsDQogICAgICAgICAgIkJ1cyBEcml2ZXIiLA0KICAgICAgICAgICJSZXRhaWwgTWFuYWdlbWVudCIsDQogICAgICAgICAgIldhaXRlci9XYWl0cmVzcyIsDQogICAgICAgICAgIkZsaWdodCBBdHRlbmRhbnQiLA0KICAgICAgICAgICJDbGVyaWNhbCIsDQogICAgICAgICAgIlJlbGlnaW91cyIsDQogICAgICAgICAgIkNsZXJneSIpLA0KTGFib3Jlcj1jKCJDb25zdHJ1Y3Rpb24iLA0KICAgICAgICAgICJMYWJvcmVyIiwNCiAgICAgICAgICAiU2tpbGxlZCBMYWJvciIsDQogICAgICAgICAgIkxhbmRzY2FwaW5nIiwNCiAgICAgICAgICAiSG9tZW1ha2VyIiwNCiAgICAgICAgICAiRmlyZW1hbiIsDQogICAgICAgICAgIkV4ZWN1dGl2ZSIsDQogICAgICAgICAgIlRlYWNoZXIncyBBaWRlIiwNCiAgICAgICAgICAiQ29tcHV0ZXIgUHJvZ3JhbW1lciIsDQogICAgICAgICAgIkFkbWluaXN0cmF0aXZlIEFzc2lzdGFudCIsDQogICAgICAgICAgIlByb2Zlc3Npb25hbCIsDQogICAgICAgICAgIkFjY291bnRhbnQvQ1BBIiwNCiAgICAgICAgICAiVHJhZGVzbWFuIC0gQ2FycGVudGVyIiwNCiAgICAgICAgICAgICJUcmFkZXNtYW4gLSBNZWNoYW5pYyIsDQogICAgICAgICAgICAiVHJhZGVzbWFuIC0gRWxlY3RyaWNpYW4iLA0KICAgICAgICAgICAgIlRyYWRlc21hbiAtIFBsdW1iZXIiLA0KICAgICAgICAgICJQaWxvdCAtIFByaXZhdGUvQ29tbWVyY2lhbCIpLA0KSGlnaGVyRWRKb2JzPWMoIkFyY2hpdGVjdCIsDQogICAgICAgICAgICAgICAiQmlvbG9naXN0IiwNCiAgICAgICAgICAgICAgICJFbmdpbmVlciAtIEVsZWN0cmljYWwiLA0KICAgICAgICAgICAgICAgIkVuZ2luZWVyIC0gTWVjaGFuaWNhbCIsDQogICAgICAgICAgICAgICAiRW5naW5lZXIgLSBDaGVtaWNhbCIsDQogICAgICAgICAgICAgICAiSnVkZ2UiLCAiVGVhY2hlciIsDQogICAgICAgICAgICAgICAiU2NpZW50aXN0IiwNCiAgICAgICAgICAgICAgICJQcm9mZXNzb3IiLA0KICAgICAgICAgICAgICAgIkF0dG9ybmV5IiwgIkFuYWx5c3QiLCAiQWNjb3VudGFudC9DUEEiDQogICAgICAgICAgICAgICApLA0KQ2l2aWxTZXJ2aWNlPWMoIkNpdmlsIFNlcnZpY2UiLA0KICAgICAgICAgICAgICAgIk1pbGl0YXJ5IE9mZmljZXIiLA0KICAgICAgICAgICAgICAgIlBvbGljZSBPZmZpY2VyL0NvcnJlY3Rpb24gT2ZmaWNlciIsDQogICAgICAgICAgICAgICAiTWlsaXRhcnkgRW5saXN0ZWQiKSwNCk90aGVyPWMoIk90aGVyIiwgIiIpDQopDQoNCmdncGxvdChkYXRhPXN1YnNldChsb2FucywgR3JvdXBlZE9jY3VwYXRpb24gIT0gJ090aGVyJyAmICFpcy5uYShHcm91cGVkT2NjdXBhdGlvbikpLCB4PUdyb3VwZWRPY2N1cGF0aW9uLCBhZXMocmVvcmRlcl9zaXplKEdyb3VwZWRPY2N1cGF0aW9uKSkpICsNCiAgICBnZW9tX2Jhcihjb2xvdXI9JyMyNDMyM2UnLCBmaWxsPScjMDJjY2JhJykrDQogICAgZ2d0aXRsZSgiQm9ycm93ZXJzIGJ5IE9jY3VwYXRpb24iKSsNCiAgICBsYWJzKHg9IiIsIHkgPSAiTnVtYmVyIG9mIGxvYW5zIikrDQogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEsdmp1c3Q9MC41KSkNCmBgYA0KDQpMZXQgdXMgdGFrZSBhIGNsb3NlciBsb29rLCBob3cgbXVjaCBtb25leSBpcyBuZWVkZWQsIGRlcGVuZGluZyBvbiB0aGUgZmFjdCB0aGF0IGEgYm9ycm93ZXIgaXMgL2lzIG5vdCBhIGhvbWUgb3duZXIgYW5kIGhpcyBvY2N1cGF0aW9uYWwgZ3JvdXAuDQoNCiMjI0xvYW4gYW1vdW50IGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaG9tZW93bmVyIHN0YXR1cw0KYGBge3IgOCAtIExvYW4gYW1vdW50IGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaG9tZW93bmVyIHN0YXR1c30NCmdncGxvdChhZXMoeCA9IElzQm9ycm93ZXJIb21lb3duZXIsIHkgPSBMb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgICAgZGF0YSA9IGxvYW5zKSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gJ3BvaW50Jywgc2hhcGUgPSA0KQ0KDQpnZ3Bsb3QoYWVzKHggPSBHcm91cGVkT2NjdXBhdGlvbiwgeSA9IExvYW5PcmlnaW5hbEFtb3VudCksDQogICAgICBkYXRhID0gbG9hbnMpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTAuNSx2anVzdD0wLjUpKSsNCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gSXNCb3Jyb3dlckhvbWVvd25lcikpDQpgYGANCkluIHRoZSBwbG90IGFib3ZlLCB3ZSBjYW4gc2VlIHRoYXQgc3R1bmRlbnRzIG5lZWQgdGhlIHNtYWxsZXIgYW1vdW50cyBvZiBtb25leS4gVGhlIGhpZ2hlciB0aGUgbG9hbiBnZXRzLCB0aGUgbW9yZSBob21lb3duZXJzIGFyZSB0aGUgYm9ycm93ZXJzLg0KDQpUbyBnZXQgYmV0dGVyIGluZm9ybWF0aW9uLCB3ZSB3YW50IHRvIGxpbWl0IHRoZSBsb2FuIHRvIGEgbWF4aW11bSBvZiA1MDAwLg0KIyMjTGltaXRlZCBsb2FuIGFtb3VudCBieSBncm91cGVkIG9jY3VwYXRpb24gYW5kIGhvbWVvd25lciBzdGF0dXMNCmBgYHtyIDkgLSBMaW1pdGVkIGxvYW4gYW1vdW50IGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaG9tZW93bmVyIHN0YXR1c30NCmdncGxvdChhZXMoeCA9IEdyb3VwZWRPY2N1cGF0aW9uLCB5ID0gTG9hbk9yaWdpbmFsQW1vdW50KSwNCiAgICAgIGRhdGEgPSBsb2FucykgKw0KICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSxoanVzdCA9IDAuNSx2anVzdCA9IDAuNSkpKw0KICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBJc0JvcnJvd2VySG9tZW93bmVyKSkrDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxMDAwLCA1MDAwKSkNCmBgYA0KRm9yIG1vbmV5IGxlc3MgdGhhbiA1MDAwIHRoZSBtYWpvcml0eSBvZiB0aGUgYm9ycm93ZXJzIGFyZSBub3QgaG9tZSBvd25lcnMuIEFsdGhvdWdoIGluIHNvbWUgb2NjdXBhdGlvbmFsIGdyb3VwcyB0aGVyZSBhcmUgYSBsb3Qgb2YgaG9tZSBvd25lcnMuIFRoaXMgbWF5IGJlIGNhdXNlZCBieSBvdXIgb2NjdXBhdGlvbmFsIGdyb3VwaW5nLCB3aGljaCBjb250YWlucyBwcm9mZXNzaW9ucyB3aXRoIGEgd2lkZSBpbmNvbWUgcmFuZ2UuIEZvciBleGFtcGxlIGluIHRoZSBncm91cCAnTWVkaWNhbF9IZWFsdGgnLCB0aGVyZSBhcmUgZG9jdG9ycyBhbmQgbnVyc2VzIGV0Yy4NCg0KIyMjUHJvc3BlciByYXRpbmcNCmBgYHtyIHByb3NwZXIgcmF0aW5nfQ0KIy0tLS0tLS0tLS0tLS0tLS0tY3JlYXRlIGEgbmV3IERGIHdpdGggaW5mbyBhYm91dCB0aGUgcHJvc3BlciByYXRpbmcgYW5kIHRoZSBhbW91bnQNCg0KbGlicmFyeShkcGx5cikNCmNyZWRpdHNCeUdyYWRlIDwtIGdyb3VwX2J5KGxvYW5zLCBQcm9zcGVyUmF0aW5nLi5udW1lcmljLikNCmNyZWRpdHNCeUdyYWRlIDwtIHN1bW1hcmlzZShjcmVkaXRzQnlHcmFkZSwNCiAgICBtZWFuX2Ftb3VudCA9IG1lYW4oTG9hbk9yaWdpbmFsQW1vdW50KSwgDQogICAgbWVkaWFuX2Ftb3VudCA9IG1lZGlhbihMb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgIG1pbl9hbW91bnQgPSBtaW4oTG9hbk9yaWdpbmFsQW1vdW50KSwNCiAgICBtYXhfYW1vdW50ID0gbWF4KExvYW5PcmlnaW5hbEFtb3VudCksDQogICAgbiA9IG4oKSkgDQoNCmNyZWRpdHNCeUdyYWRlDQpgYGANCg0KIyMjQm9ycm93ZXIgUmF0ZSAtIFByb3NwZXIgU2NvcmUNCmBgYHtyIDEwIC0gYm9ycm93ZXIgcmF0ZSAtIHByb3NwZXIgc2NvcmV9DQpnZ3Bsb3QoYWVzKHg9Qm9ycm93ZXJSYXRlLCB5PVByb3NwZXJTY29yZSksIGRhdGEgPSBsb2FucykrDQogIGdlb21fbGluZSgpKw0KICBnZW9tX3Ntb290aCgpKw0KICBnZ3RpdGxlKCJMaW5lIFBsb3Qgb2YgYm9ycm93ZXIgcmF0ZSBhbmQgcHJvc3BlciBzY29yZSIpDQpgYGANCk5vIHN1cnByaXNlcyBoZXJlLCB0aGUgYmV0dGVyIHRoZSBQcm9zcGVyIFNjb3JlLCB0aGUgYmV0dGVyIHRoZSBib3Jyb3dlciByYXRlLg0KDQojIyNQcm9zcGVyIHJhdGluZyBpZiB0aGUgaW5jb21lIGlzIHZlcmlmaWFibGUNCldlIHN1Z2dlc3QgdGhhdCB0aGUgcHJvc3BlciByYXRpbmcgaXMgYmV0dGVyIGlmIHRoZSBpbmNvbWUgaXMgdmVyaWZpYWJsZS4NCmBgYHtyIDExIC0gUHJvc3BlciByYXRpbmcgaW4gcGVyY2VudCBieSB2ZXJpZmlhYmxlIGluY29tZX0NCmdncGxvdChhZXMoeCA9IFByb3NwZXJSYXRpbmcuLm51bWVyaWMuLCB5ID0gLi5jb3VudC4uL3N1bSguLmNvdW50Li4pKSwgZGF0YSA9IHN1YnNldChsb2FucywgIWlzLm5hKEluY29tZVZlcmlmaWFibGUpKSkgKw0KICBnZW9tX2ZyZXFwb2x5KGFlcyhjb2xvcj1JbmNvbWVWZXJpZmlhYmxlKSkgKyANCiAgeGxhYignUHJvc3BlciBSYXRpbmcnKSArIA0KICB5bGFiKCdQZXJjZW50YWdlIG9mIEJvcnJvd2VycyB3aXRoIHRoYXQgUHJvc3BlciBSYXRpbmcnKSsNCiAgZ2d0aXRsZSgiUHJvc3BlciByYXRpbmcgaW4gcGVyY2VudCBieSB2ZXJpZmlhYmxlIGluY29tZSIpDQpgYGANClllcywgb3VyIHN1Z2dlc3Rpb24gaXMgcmlnaHQuDQoNCiMjI0VtcGxveW1lbnQgc3RhdHVzIGR1cmF0aW9uDQpOZXh0IHdlIHdhbnQgdG8gdGFrZSBhIGxvb2sgYXQgdGhlIGNyZWRpdCBncmFkZSBhbmQgdGhlIGVtcGxveW1lbnQgc3RhdHVzIGR1cmF0aW9uLiBXZSBzdWdnZXN0IHRoZSBsb25nZXIgdGhlIGVtcGxveW1lbnQgc3RhdHVzLCB0aGUgYmV0dGVyIGlzIHRoZSBjcmVkaXQgZ3JhZGUuDQpgYGB7ciAxMiAtIGNyZWRpdCBncmFkZSBieSBlbXBsb3ltZW50IHN0YXR1cyBkdXJhdGlvbn0NCg0Kc3VtbWFyeShsb2FucyRFbXBsb3ltZW50U3RhdHVzRHVyYXRpb24pDQoNCmdncGxvdChhZXMoeCA9IENyZWRpdEdyYWRlLCB5ID0gRW1wbG95bWVudFN0YXR1c0R1cmF0aW9uKSwNCiAgICAgICBkYXRhID0gbG9hbnMpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSAgDQogICAgICAgICAgICAgICAgICBFbXBsb3ltZW50U3RhdHVzKSkrDQogIGdndGl0bGUoIkNyZWRpdCBncmFkZSBieSBlbXBsb3ltZW50IHN0YXR1cyBkdXJhdGlvbiIpDQpgYGANCkFzIHdlIGNhbiBzZWUgaW4gdGhlIHBsb3QgYWJvdmUsIG1vc3Qgb2YgdGhlIGxvYW4gdGFrZXJzIGFyZSBmdWxsLXRpbWUgZW1wbG95ZWVzLiBCdXQgYWxzbyB0aGVyZSBpcyBhIGxvdCBvZiBEYXRhIG1pc3NpbmcuDQpUbyBnZXQgYSBjbGVhcmVyIHBpY3R1cmUsIHdlIHdhbnQgZG8gc2F2ZSB0aGUgZW1wbG95bWVudCBzdGF0dXMgZHVyYXRpb24gaW50byBidWNrZXRzLiANCg0KYGBge3IgMTMgLSBjcmVkaXQgZ3JhZGUgYnkgZW1wbG95bWVudCBzdGF0dXMgZHVyYXRpb24gaW4gYnVja2V0c30NCg0KIyBzYXZlIGVtcGxveW1lbnQgc3RhdHVzIGR1cmF0aW9uIGludG8gYnVja2V0cy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpsb2FucyRidWNrZXRfRW1wbG95bWVudFN0YXR1c0R1cmF0aW9uIDwtIGN1dChsb2FucyRFbXBsb3ltZW50U3RhdHVzRHVyYXRpb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygwLCAxMiwgMjQsIDM2LCA0OCwgNTAsIDYyLCA3NCwgODYsIDk4LCAxMTAsIDEyMiwgMTM0LCAxNDYsIDc1NSkpDQoNCmdncGxvdChhZXMoeCA9IENyZWRpdEdyYWRlLCB5ID0gYnVja2V0X0VtcGxveW1lbnRTdGF0dXNEdXJhdGlvbiksIGRhdGEgPSBsb2FucykgKyANCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBFbXBsb3ltZW50U3RhdHVzKSkrDQogIGdndGl0bGUoIkNyZWRpdCBncmFkZSBieSBlbXBsb3ltZW50IHN0YXR1cyBkdXJhdGlvbiBpbiBidWNrZXRzIikNCmBgYA0KVGhpcyBwbG90IGlzIG5vdCBtdWNoIGJldHRlciwgYWx0b3VnaCBpdCBpcyBlYXNpZXIgdG8gcmVhZC4gV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGRhdGEgbWlzc2luZyBhbmQgdGhhdCBtb3N0IG9mIHRoZSBsb2FuIHRha2VycyBhcmUgZnVsbCB0aW1lIGVtcGxveWVlcy4NCg0KYGBge3IgMTQgLSBoaXN0b2dyYW0gbG9hbkFtb3VudHN9DQpnZ3Bsb3QoYWVzKHggPSBMb2FuT3JpZ2luYWxBbW91bnQsIGZpbGwgPSBMb2FuU3RhdHVzKSwgZGF0YSA9IGxvYW5zKSArDQogICAgZmFjZXRfd3JhcCh+VGVybSkgKw0KICAgIGdlb21faGlzdG9ncmFtKGFlcyhjb2xvciA9IExvYW5TdGF0dXMpKSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICdxdWFsJykgKw0KICAgIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiBsb2FuIEFtb3VudHMgYnkgc3RhdHVzIGFuZCB0ZXJtcyIpDQpgYGANCkluIHRoZSBwbG90IGFib3ZlIHdlIGNhbiBzZWUgdGhhdCBtb3N0IG9mIHRoZSBsb2FucyBhcmUgbWlkLXRlcm0uIA0KDQojIyNEZWJ0IHRvIGluY29tZSByYXRpbw0KYGBge3IgMTUgLSBEZXB0IHRvIGluY29tZSByYXRpb30NCmdncGxvdChkYXRhID0gbG9hbnMsIGFlcyh4ID0gRGVidFRvSW5jb21lUmF0aW8pKSArICAgICAgICAgICAgICAgIA0KICAgICAgICBnZW9tX2hpc3RvZ3JhbShjb2xvdXIgPSAnIzI0MzIzZScsIGZpbGwgPSAnIzAyY2NiYScsIGJpbndpZHRoID0gMC4wMDUpICsNCiAgICAgICAgeGxpbSgwLCBxdWFudGlsZShsb2FucyREZWJ0VG9JbmNvbWVSYXRpbywgcHJvYiA9IDAuNSwgbmEucm0gPSBUUlVFKSkgKw0KICAgICAgICBnZ3RpdGxlKCJEZWJ0IFRvIEluY29tZSBSYXRpbyIpICsNCiAgICAgICAgeGxhYigiRGVidCB0byBJbmNvbWUgUmF0aW8iKSArDQogICAgICAgIHlsYWIoIkNvdW50IikNCiAgICAgICAgc3VtbWFyeShsb2FucyRsb2FuX2luY29tZV9yYXRpbykNCmBgYCANCiMjI0xlbmRlciB5aWVsZA0KV2Ugc3VnZ2VzdCB0aGF0IHRoZXJlIGlzIGEgaGlnaGVyIGxlbmRlciB5aWVsZCwgaWYgdGhlIGJvcnJvd2VyIHJhdGUgaXMgaGlnaGVyLg0KYGBge3IgMTYgLSBsZW5kZXIgeWllbGQgYW5kIGJvcnJvd2VyIHJhdGV9DQpsaWJyYXJ5KGRwbHlyKQ0KZ2dwbG90KGxvYW5zLCBhZXMoeCA9IExlbmRlcllpZWxkLCB5ID0gQm9ycm93ZXJSYXRlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMS8yMCwgY29sb3VyID0gJyMwMmNjYmEnKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIHF1YW50aWxlKGxvYW5zJExlbmRlcllpZWxkLCAwLjc1KSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKCANCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCAsIHF1YW50aWxlKGxvYW5zJEJvcnJvd2VyUmF0ZSwgMC43NSkpKSArDQogIGdndGl0bGUoJ0xlbmRlciB5aWVsZCBhbmQgYm9ycm93ZXIgcmF0ZScpDQpgYGANClllcywgd2UgY2FuIHNlZSBjbGVhcmx5IHRoZSBoaWdoZXIgdGhlIGJvcnJvd2VyIHJhdGUsIHRoZSBoaWdoZXIgdGhlIGxlbmRlciB5aWVsZC4NCg0KIyMjTGVuZGVyIHlpZWxkIGJ5IG51bWJlciBvZiBJbnZlc3RvcnMNCmBgYHtyIDE3IC0gTGVuZGVyIFlpZWxkIGJ5IG51bWJlciBvZiBJbnZlc3RvcnN9DQpnZ3Bsb3QoYWVzKHggPSBCb3Jyb3dlclJhdGUsIHkgPSBJbnZlc3RvcnMpLCBkYXRhPWxvYW5zKSArIA0KICBnZW9tX3BvaW50KGFlcyhjb2xvcj1MZW5kZXJZaWVsZCkpKw0KICB5bGltKDAsIDYwMCkrDQogIGdndGl0bGUoJ0xlbmRlciB5aWVsZCBieSBudW1iZXIgb2YgaW52ZXN0b3JzJykNCmBgYA0KSW4gdGhlIHBsb3QgYWJvdmUgd2UgY2FuIHNlZSBxdWl0ZSB3ZWxsLCB0aGF0IHRoZSBpbnZlc3RvcnMgeWllbGQgZ2V0cyBoaWdoZXIgdGhlIGhpZ2hlciB0aGUgYm9ycm93ZXIgcmF0ZSBnZXRzLg0KDQpgYGB7ciAxOCAtIE51bWJlciBvZiBJbnZlc3RvcnMgcGVyIExvYW59DQpnZ3Bsb3QoYWVzKHggPSBsb2FucyRJbnZlc3RvcnMsIHkgPSAuLmNvdW50Li4pLCBkYXRhID0gbG9hbnMpICsNCiAgZ2VvbV9mcmVxcG9seShhZXMoY29sb3IgPSBJbnZlc3RvcnMpLCBiaW53aWR0aD0wLjEpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI1MCksIGJyZWFrcyA9IHNlcSgwLCA1MCwgMjUwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDc1MCwgNTApKSsNCiAgeWxpbSgwLCA3NTApKw0KICB4bGltKDEsIDI1MCkrDQogIHhsYWIoJ251bWJlciBvZiBJbnZlc3RvcnMnKSArIA0KICB5bGFiKCdjb3VudCcpICsNCiAgZ2d0aXRsZSgnTnVtYmVyIG9mIEludmVzdG9ycyBwZXIgTG9hbicpDQpgYGANClRoZSBtYWpvcml0eSBvZiB0aGUgSW52ZXN0b3JzIGlzIGp1c3Qgb25lIHBlcnNvbi4gSW4gdGhlIHBsb3QgYWJvdmUsIHdlIGxpbWl0IHRoZSBudW1iZXIgb2YgbG9hbnMgZ2l2ZW4gdG8gMTUwMCBpbiBvcmRlciB0byBnZXQgYSBiZXR0ZXIgcGljdHVyZSBvZiBsb2FucyBnaXZlbiBieSBtb3JlIHRoZW4gb25lIGludmVzdG9yLiAgDQoNCiMjI0N1cnJlbnQgZGVsaW5xdWVuY2llcyBieSBjcmVkaXQgZ3JhZGUNCmBgYHtyIDE5IC0gQ3VycmVudCBEZWxpbnF1ZW5jaWVzIGJ5IGNyZWRpdCBncmFkZX0NCiAgICBxcGxvdCh4PUNyZWRpdEdyYWRlLCB5PURlbGlucXVlbmNpZXNMYXN0N1llYXJzLA0KICAgICAgICBkYXRhPWxvYW5zLCBnZW9tPSdib3hwbG90JykrDQogICAgeWxpbSgwLCAyNSkNCmBgYA0KQXMgd2UgY2FuIHNlZSBpbiB0aGUgcGxvdCBhYm92ZSwgdGhlIGJldHRlciB0aGUgY3JlZGl0IGdyYWRlLCBsZXNzIGRlbGlucXVlbmljZXMuDQoNCiMjI0xvYW4gYW1vdW50IGJ5IHRlcm0sIGdyb3VwZWQgYnkgZ3JvdXBlZCBvY2N1cGF0aW9uIGFuZCBjYXRlZ29yeQ0KYGBge3IgMjAgLSBsb2FuIGFtb3VudCBieSB0ZXJtLCBncm91cGVkIGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgY2F0ZWdvcnl9DQpnZ3Bsb3QoYWVzKHggPSBUZXJtLCB5ID0gTG9hbk9yaWdpbmFsQW1vdW50KSwgZGF0YSA9IGxvYW5zKSArDQogICAgZmFjZXRfd3JhcCh+R3JvdXBlZE9jY3VwYXRpb24pICsNCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IENhdGVnb3J5KSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDEwMDAsIDUwMDApKQ0KYGBgDQpUaGUgcGxvdCBhYm92ZSBnaXZlcyB1cyBhIG5pY2Ugb3ZlcnZpZXcuIEFzIHdlIGNhbiBzZWUgbW9zdCBvZiB0aGUgbG9hbnMgYXJlIG1pZCB0ZXJtIGxvYW5zIHdpdGggYSBkdXJhdGlvbiBvZiAzNiBtb250aC4gVGhlIHVzYWdlcyBvZiB0aGUgbG9hbnMgYXJlIHdlbGwgbWl4ZWQuIA0KDQoNCmBgYHtyIDIxIC0gRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIGFuZCBvY2N1cGF0aW9uIG9mIGJvcnJvd2VyfQ0KZ2dwbG90KGFlcyhFc3RpbWF0ZWRSZXR1cm4sIEVzdGltYXRlZExvc3MpLCANCiAgICAgICBkYXRhPXN1YnNldChsb2FucywgR3JvdXBlZE9jY3VwYXRpb24gIT0gJ090aGVyJyANCiAgICAgICAgICAgICAgICAgICAmICFpcy5uYShHcm91cGVkT2NjdXBhdGlvbikNCiAgICAgICAgICAgICAgICAgICAmICFpcy5uYShJbmNvbWVSYW5nZSkNCiAgICAgICAgICAgICAgICAgICAmIEluY29tZVJhbmdlICE9ICdOb3QgZGlzcGxheWVkJw0KICAgICAgICAgICAgICAgICAgJiBJbmNvbWVSYW5nZSAhPSAnTm90IGVtcGxveWVkJykpKw0KICBnZW9tX3BvaW50KGFlcyhzaXplPUluY29tZVJhbmdlLCBjb2xvdXI9R3JvdXBlZE9jY3VwYXRpb24pKSsNCiAgZ2d0aXRsZSgnRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIG9mIGJvcnJvd2VyJykNCmBgYA0KVGhlIHBsb3QgYWJvdmUgZ2l2ZXMgYW4gb3ZlcnZpZXcgb2YgdGhlIGVzdGltYXRlZCByZXR1cm4gYW5kIGVzdGltYXRlZCBsb3NzIGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaW5jb21lIHJhbmdlLg0KV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgc21hbGwgZ3JvdXAgd2hlcmUgdGhlIGVzdGltYXRlZCBsb3NzIGlzIGhpZ2ggYW5kIHRoZXJlIGlzIG5vdCBhbiBlc3RpbWF0ZWQgcmV0dXJuLiBCdXQgd2UgY2FuJ3Qgc2VlIHRoYXQgdGhpcyBpcyBoYXBwZW5pbmcgb25seSB0byBhIHNwZWNpYWwgb2NjdXB0aW9uIGdyb3VwIG9yIGluY29tZSByYW5nZSBpbiB0aGVyZS4gTW9zdCBvZiB0aGUgZXN0aW1hdGVkIHJldHVybnMgYW5kIGVzdGltYXRlZCBsb3NzZXMgYXJlIGJldHdlZW4gMCBhbmQgMC4xLg0KDQpCZWNhdXNlIHRoZSBwbG90IGlzIGhhcmQgdG8gcmVhZCBkdWUgdG8gdGhlIGRlbnNpdHkgb2YgaW5mb3JtYXRpb24sIHdlIHdpbGwgdGFrZSBhIGNsb3NlciBsb29rIGF0IG9uIG9jY3VwYXRpb24gZ3JvdXAgLSB0aGUgc3R1ZGVudHMuDQoNCmBgYHtyIDIyIC0gRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIGZvciBzdHVkZW50c30NCmdncGxvdChhZXMoRXN0aW1hdGVkUmV0dXJuLCBFc3RpbWF0ZWRMb3NzKSwgDQogICAgICAgZGF0YT1zdWJzZXQobG9hbnMsIEdyb3VwZWRPY2N1cGF0aW9uID09ICdTdHVkZW50JyANCiAgICAgICAgICAgICAgICAgICAmIEluY29tZVJhbmdlICE9ICdOb3QgZGlzcGxheWVkJw0KICAgICAgICAgICAgICAgICAgJiBJbmNvbWVSYW5nZSAhPSAnTm90IGVtcGxveWVkJykpKw0KICBnZW9tX3BvaW50KGFlcyhzaXplPUluY29tZVJhbmdlLCBjb2xvdXI9SW5jb21lUmFuZ2UpKSsNCiAgZ2d0aXRsZSgnRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIGZvciBzdHVkZW50cycpDQpgYGANClRoaXMgcGxvdCBzaG93cyB0aGF0IHRoZXJlIGFyZSBzb21lIHN0dWRlbnRzIHRoYXQgc2VlbSB0byBlYXJuIGFscmVhZHkgYSBsb3Qgb2YgbW9uZXksIGFsdG91Z2h0IHRoZSBtYWpvcml0eSBlYXJucyBiZXR3ZWVuICQxIC0gMjQ5OTkuDQoNCmBgYHtyIDIzIC0gU2VydmljZSBmZWVzIGFuZCBpbnRlcmVzdGFuZCBmZWVzIGR1ZSB0byBjdXN0b21lciBwYXltZW50c30NCmdncGxvdChsb2FucywgYWVzKExQX0N1c3RvbWVyUGF5bWVudHMsIExQX0ludGVyZXN0YW5kRmVlcykpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID1MUF9TZXJ2aWNlRmVlcyksIHNpemUgPSAxKSArDQogIGNvb3JkX2VxdWFsKCkNCmBgYA0KVGhpcyBwbG90IHNob3dzIHRoYXQgdGhlIGxvd2VyIHRoZSBjdXN0b21lciBwYXltZW50cyBhcmUgdGhlIGxvd2VyIHRoZSBzZXJ2aWNlIGZlZXMgYW5kIGludGVyZXN0IGZlZXMgYXJlLg0KDQojIyNIZWF0bWFwDQpgYGB7ciAyNCAtIFNpbXBsZSBIZWF0bWFwIG9mIExvYW5zIGJ5IG9jY3VwYXRpb24gYW5kIGNhdGVnb3J5IH0NCmdncGxvdChkYXRhID0gbG9hbnMsIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBHcm91cGVkT2NjdXBhdGlvbikpICsNCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gTG9hbk9yaWdpbmFsQW1vdW50KSkgKw0KICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NjAsaGp1c3Q9MSx2anVzdD0wLjkpKQ0KYGBgDQpJbiB0aGUgcGxvdCBhYm92ZSB3ZSBnZXQgYSBuaWNlIG92ZXJ2aWV3IG9mIHRoZSBjYXRlZ29yeSBvZiBsb2FucyBhbmQgdGhlIGdyb3VwZWQgb2NjdXBhdGlvbnMuIA0KDQojI1N1bW1hcnkNCkJlY2F1c2Ugb2YgdGhlIGJpZyBhbW91bnQgb2YgdmFyaWFibGVzIGl0IHRvb2sgc29tZSB0aW1lLCB0byByZWFkIHRocm91Z2ggdGhlIGV4cGxhbmF0aW9ucyBvZiB0aGUgcHJvc3BlciBsb2FuIGRhdGEuIFRvIGdldCBzdGFydGVkIHdlIGV4cGxvcmVkIHNvbWUgZGlmZmVyZW50IHZhcmlhYmxlcy4gDQpJbiBvcmRlciB0byBnZXQgbmljZSBwbG90cywgd2UgaGFkIHRvIGNvbnZlcnQgc29tZSB2YWx1ZXMuIEZvciBleGFtcGxlIHRoZSBvcmlnaW4gZGF0ZSB0byB5ZWFyLCB0aGUgbnVtZXJpYyBjYXRlZ29yaWVzIGludG8gcmVhZGFibGUgY2F0ZWdvcmllcyBhbmQgdGhlIGpvYiBkdXJhdGlvbiBtb250aHMgd2hlcmUgc3VtbWFyaXplZCBpbiBidWNrZXRzDQpJdCB3YXMgaW50ZXJyZXN0aW5nIHRvIHNlZSB0aGF0IGZyb20gMjAxMSBvbiBib3Jyb3dlcnMgbmVlZGVkIGhpZ2hlciBsb2FucyB3aXRoIGxvbmdlciB0ZXJtcy4gDQpJdCB3YXMgcXVpdGUgYSBzdXJwcmlzZSB0aGF0IHRoZXJlIGlzIG5vIGJpZyBkaWZmZXJlbmNlIGJldHdlZW4gaG9tZSBvd25lcnMgYW5kIG5vbiBob21lIG93bmVycywgYmVjYXVzZSB3ZSBzdWdnZXN0ZWQgdGhhdCBob21lIG93bmVycyBhcmUgZmluYW5jaWFsbHkgbW9yZSBzdHJvbmcgYW5kIGRvbid0IG5lZWQgc21hbGwgbG9hbnMuIA0KRm9yIHRoZSBvdGhlciB2YXJpYWJsZXMgSSBjb3VsZCBub3QgZmluZCBhIGxvdCBvZiBzdXJwcmlzaW5nIGZhY3RzLiBGb3IgZXhhbXBsZSB0aGUgd29yc2UgdGhlIGNyZWRpdCBncmFkZSBpcywgdGhlIGhpZ2hlciB0aGUgZGVsaW5xdWVuY2llcyBpbiB0aGUgbGFzdCA3IHllYXJzIGFyZSBvciB0aGF0IHRoZSBsZW5kZXIgeWllbGQgZ2V0cyBsb3dlciB0aGUgaGlnaGVyIHRoZSBudW1iZSBvZiBpbnZlc3RvcnMgZ2V0Lg0KSXQgd291bGQgYmUgbmljZSBpZiB0aGVyZSB3ZXJlIG5vdCBiaWcgZ3JvdXBzIGxpa2UgJ25hJyBvciAnb3RoZXInIGluIHRoZSBvY2N1cGF0aW9uIGFuZCBjYXRlZ29yeSBncm91cC4gTWF5YmUgdGhlcmUgY291bGQgYmUgYWxzbyBkYXRhIGFib3V0IHRoZSBhZ2UgYW5kIGdlbmRlciBvZiB0aGUgYm9ycm93ZXIgcHJvdmlkZWQsIHdoaWNoIG1heSBsZWFkIHRvIGludGVyZXN0aW5nIGZpbmRpbmdzLg0K